/*
 * Copyright (c) 2020 - present, Inspur Genersoft Co., Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *        http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.inspur.edp.web.formmetadata.replication;


import com.inspur.edp.i18n.resource.api.metadata.LinguisticResource;
import com.inspur.edp.i18n.resource.api.metadata.ResourceItem;
import com.inspur.edp.lcm.metadata.api.entity.GspMetadata;
import com.inspur.edp.lcm.metadata.api.entity.MetadataDto;
import com.inspur.edp.web.common.customexception.WebCustomException;
import com.inspur.edp.web.common.entity.ResultCode;
import com.inspur.edp.web.common.entity.ResultMessage;
import com.inspur.edp.web.common.io.FileUtility;
import com.inspur.edp.web.common.logger.WebLogger;
import com.inspur.edp.web.common.metadata.MetadataUtility;
import com.inspur.edp.web.common.utility.StringUtility;
import com.inspur.edp.web.formmetadata.entity.ResourceMetadataType;
import com.inspur.edp.web.formmetadata.metadata.FormMetadataContent;
import com.inspur.edp.web.formmetadata.metadata.FormMetadataContentService;
import com.inspur.edp.web.formmetadata.metadata.formdom.FormDOM;
import org.apache.commons.lang3.StringUtils;

import java.util.Arrays;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * 表单元数据复制器
 *
 * @author guozhiqi
 */
public class FormMetadataReplicator {
    private static final boolean IsDebug = true;

    /**
     * 表单复制接口，目前仅支持同一个BO内复制
     * 针对表单元数据复制，需要判断其是否已存在，如果已经存在，那么不允许其进行复制操作
     * 如果是表单元数据关联的其他元数据，那么不判断其是否已存在，如果已存在，那么执行先删除操作
     *
     * @param sourceFormMetadata 待复制表单元数据
     * @param sourceProjectName  待复制表单元数据所属工程名称
     * @param targetMetadataCode 目标元数据编号
     * @param targetMetadataName 目标元数据名称
     * @param targetProjectName  目标元数据所在工程名称
     */
    public final ResultMessage<String> replicate(GspMetadata sourceFormMetadata, String sourceProjectName,
                                                 String targetMetadataCode, Map<String, String> targetMetadataNameLanguage, String targetProjectName) {

        // 初始化元数据复制上下文
        MetadataReplicationContext metadataReplicationContext = MetadataReplicationContextService.create(sourceProjectName, sourceFormMetadata, targetMetadataCode, targetProjectName, targetMetadataNameLanguage);
        // 依据是否相同工程更新对应的目标元数据路径
        MetadataReplicationContextService.updateMetadataReplicationContext(metadataReplicationContext);

        // 依据复制上下文 执行表单复制
        return executeReplicate(metadataReplicationContext);
    }


    /**
     * 复制表单元数据及关联的元数据
     * 目前仅支持同一个BO下复制（同一工程、不同工程）
     *
     * @param context 元数据复制上下文参数
     */
    private ResultMessage<String> executeReplicate(final MetadataReplicationContext context) {
        GspMetadata sourceFormMetadata = context.getSourceMetadata();


        MetadataDto targetMetadataDescription = context.getTargetMetadataDescription();
        GspMetadata replicateFormMetadata = null;
        try {
            // 复制表单元数据 为了避免后续保存中间步骤出错，因此针对复制元数据，将里面必须的元数据置空
            replicateFormMetadata = MetadataCloneManager.cloneMetadata(context);

            //执行复制前参数必要验证
            MetaDataReplicateBeforeValidator beforeValidator = new MetaDataReplicateBeforeValidator();
            ResultMessage<String> validateResult = beforeValidator.validate(replicateFormMetadata);
            if (!validateResult.isSuccess()) {
                return validateResult;
            }

            // 元数据创建前处理
            // 如果待保存元数据所在文件夹不存在，则创建文件夹
            String replicateFormMetadataAbsolutePath = MetadataUtility.getInstance().getAbsolutePath(replicateFormMetadata.getRelativePath());
            if (!FileUtility.exists(replicateFormMetadataAbsolutePath)) {
                FileUtility.createDirectory(replicateFormMetadataAbsolutePath);
            }

            // 更新replicateFormMetadataContent
            FormMetadataContent replicateFormMetadataContent = (FormMetadataContent) replicateFormMetadata.getContent();

            FormDOM replicateFormDOM = FormMetadataContentService.getInstance().getFormContent(replicateFormMetadataContent);

            // 磁盘中创建表单元数据
            MetadataUtility.getInstance().createMetadataIfNotExistsWithDesign(replicateFormMetadata);

            // 1. 拷贝VO元数据及关联元数据
            FormMetadataVoManager.copyFormMetadataVo(context, sourceFormMetadata, targetMetadataDescription, replicateFormDOM, IsDebug);

            // 2. 表单关联状态机（从表单中获取stateMachineId）
            FormMetadataSmManager.copyFormMetadataStateMachine(context, sourceFormMetadata, targetMetadataDescription, replicateFormDOM, IsDebug);

            // 3. 表单关联命令构件及服务构件
            FormMetadataCmdManager.copy(context, sourceFormMetadata, targetMetadataDescription, replicateFormDOM);

            // 更新 Form Content
            FormMetadataContentService.getInstance().setFormContent(replicateFormMetadataContent, replicateFormDOM);

            // 前置条件：保存前，对应的元数据文件已存在
            MetadataUtility.getInstance().saveMetadataWithDesign(replicateFormMetadata);

            // 同步复制表单元数据的英文、繁体中文资源元数据
            copyResourceMetadataWithSpecificLanguage(context, sourceFormMetadata, replicateFormMetadata);
        } catch (RuntimeException ex) {
            WebLogger.Instance.error("表单复制出现错误," + ex.getMessage() + Arrays.toString(ex.getStackTrace()));
            // 执行元数据的清理工作     此处仅删除表单元数据  因此存在表单元数据级联删除动作
            if (replicateFormMetadata != null) {
                MetadataUtility.getInstance().deleteMetadataWithDesign(replicateFormMetadata.getRelativePath(), replicateFormMetadata.getHeader().getFileName());
            }

            throw new WebCustomException("", ex);
        }
        String message = null;
        if (!context.getReplicationResult().isEmpty()) {
            message = String.join("\\r\\n", context.getReplicationResult());
        }
        return ResultCode.success(message);
    }

    private static void copyResourceMetadataWithSpecificLanguage(MetadataReplicationContext context, GspMetadata sourceFormMetadata, GspMetadata replicateFormMetadata) {

        String targetZHResourceFileName = ResourceMetadataType.ZH_CH.getFileName(sourceFormMetadata.getHeader().getFileName());
        if (!MetadataUtility.getInstance().isMetaDataExistsWithMetadataPathAndFileNameWithDesign(sourceFormMetadata.getRelativePath(), targetZHResourceFileName)) {
            return;
        }

        GspMetadata zhResourceMetadata = MetadataUtility.getInstance().getMetadataWithDesign(targetZHResourceFileName, sourceFormMetadata.getRelativePath());

        // 由于直接拷贝的资源元数据的relativepath 不正确  进行调整
        zhResourceMetadata.setRelativePath(sourceFormMetadata.getRelativePath());
        MetadataUtility.getInstance().saveMetadataWithDesign(zhResourceMetadata);

        // 拷贝对应的资源元数据
        copyResourceMetadataWithSpecificLanguage(context, sourceFormMetadata, replicateFormMetadata, zhResourceMetadata, ResourceMetadataType.EN);
        copyResourceMetadataWithSpecificLanguage(context, sourceFormMetadata, replicateFormMetadata, zhResourceMetadata, ResourceMetadataType.ZH_CHT);
    }

    private static void copyResourceMetadataWithSpecificLanguage(MetadataReplicationContext context, GspMetadata sourceFormMetadata,
                                                                 GspMetadata replicateFormMetadata, GspMetadata zhResourceMetadata,
                                                                 ResourceMetadataType resourceMetadataType) {
        String sourceResourceFileName = resourceMetadataType.getFileName(sourceFormMetadata.getHeader().getFileName());
        String targetResourceFileName = resourceMetadataType.getFileName(replicateFormMetadata.getHeader().getFileName());

        if (MetadataUtility.getInstance().isMetaDataExistsWithMetadataPathAndFileNameWithDesign(sourceFormMetadata.getRelativePath(), sourceResourceFileName)) {
            GspMetadata resourceMetadata = MetadataUtility.getInstance().getMetadataWithDesign(sourceResourceFileName, sourceFormMetadata.getRelativePath());
            MetadataReplicationContext resourceContext = MetadataReplicationContextService.create(context.getSourceProjectName(), resourceMetadata, replicateFormMetadata.getHeader().getCode() + resourceMetadataType.getSuffixWithoutResExtension(),
                    replicateFormMetadata.getHeader().getName() + resourceMetadataType.getSuffixWithoutResExtension(), context.getSourceProjectName());
            resourceContext.setTargetFileName(targetResourceFileName);
            GspMetadata targetResourceMetadata = MetadataCloneManager.cloneMetadata(resourceContext);

            // 调整依赖的资源元数据参数
            LinguisticResource resourceContent = (LinguisticResource) targetResourceMetadata.getContent();
            resourceContent.getRefMetadataInfo().setId(zhResourceMetadata.getHeader().getId());
            resourceContent.getRefMetadataInfo().setCode(zhResourceMetadata.getHeader().getCode());
            resourceContent.getRefMetadataInfo().setName(zhResourceMetadata.getHeader().getName());
            resourceContent.getRefMetadataInfo().setFileName(zhResourceMetadata.getHeader().getFileName());

            String resourceNameSpace = targetResourceMetadata.getHeader().getNameSpace();
            // 调整对应的命令空间参数
            if (resourceContent.getStringResources() != null) {
                for (ResourceItem item : resourceContent.getStringResources()) {
                    if (item.getId().indexOf(resourceNameSpace) == 0) {
                        // 表示以namespace 开头 主要是调整中间的表单code部分
                        // Inspur.GS.noahtest.runtimehelp.formcopy.formcopy.Front.formcopyForm.Form.root-layout
                        String subResult = item.getId().substring(resourceNameSpace.length() + 1);
                        if (StringUtility.isNotNullOrEmpty(subResult)) {
                            String[] splitResult = subResult.split(java.util.regex.Pattern.quote("."), -1);
                            if (splitResult != null && splitResult.length > 0) {
                                splitResult[0] = replicateFormMetadata.getHeader().getCode();
                                // 重新进行组合
                                String generateResult = resourceNameSpace + "." + StringUtils.join(splitResult, ".");
                                item.setId(generateResult);
                            }
                        }

                    }
                }
            }

            MetadataUtility.getInstance().saveMetadataWithDesign(targetResourceMetadata);
        }
    }

}
